package com.joker.aigc.ai.service.impl;


import cn.hutool.core.collection.CollUtil;
import com.alibaba.nls.client.AccessToken;
import com.joker.aigc.ai.service.AiVoiceService;
import com.joker.aigc.ai.utils.SpeechRecognitionUtils;
import com.joker.aigc.chat.service.OssService;
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.data.message.SystemMessage;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.chat.StreamingChatModel;
import dev.langchain4j.model.chat.response.StreamingChatResponseHandler;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.store.embedding.EmbeddingSearchRequest;
import dev.langchain4j.store.embedding.EmbeddingSearchResult;
import dev.langchain4j.store.embedding.EmbeddingStore;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.grpc.Collections;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import reactor.core.publisher.Flux;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutionException;

/**
 * AI语音服务实现类
 * 提供语音文件上传识别和知识库文档上传功能
 *
 * @author : feixiang.li
 * @since : 2025-10-08 14:20
 */
@Service
@Slf4j
public class AiVoiceServiceImpl implements AiVoiceService {

    /**
     * 向量数据库存储，用于存储和检索文本向量
     */
    @Resource
    private EmbeddingStore<TextSegment> embeddingStore;

    @Resource
    private StreamingChatModel streamingChatModel;

    /**
     * 向量模型，用于将文本转换为向量
     */
    @Resource
    private EmbeddingModel embeddingModel;

    /**
     * OSS对象存储服务，用于上传文件到云端
     */
    @Resource
    private OssService ossService;

    /**
     * Qdrant向量数据库的集合名称
     */
    @Value("${qdrant.collection-name}")
    private String qdrantCollectionName;

    /**
     * Qdrant客户端，用于操作向量数据库
     */
    @Resource
    private QdrantClient qdrantClient;

    /**
     * OpenAI API Key，用于语音转文字服务
     */
    @Value("${langchain4j.open-ai.chat-model.api-key}")
    private String apiKey;


    /**
     * 初始化方法，在Bean创建后自动执行
     * 创建Qdrant向量数据库集合，如果集合已存在则跳过
     */
    @PostConstruct
    public void init() {
        try {
            // 配置向量参数：使用余弦相似度，向量维度为1024
            Collections.VectorParams vectorParams = Collections.VectorParams.newBuilder()
                    .setDistance(Collections.Distance.Cosine)  // 使用余弦相似度计算
                    .setSize(1024)  // 向量维度大小
                    .build();

            // 异步创建集合
            qdrantClient.createCollectionAsync(qdrantCollectionName, vectorParams);
            log.info("成功创建Qdrant集合：{}", qdrantCollectionName);
        } catch (Exception e) {
            // 如果集合已存在，会抛出异常，这里捕获并记录日志
            log.info("Qdrant集合已存在，跳过创建：{}", qdrantCollectionName);
        }
    }


    /**
     * 上传语音文件并进行智能问答
     * 流程：上传音频 -> 语音识别 -> 向量化 -> 语义检索 -> 返回相关答案
     *
     * @param file 上传的音频文件
     * @return 返回与语音内容相关的知识库答案列表（最多3条）
     */
    @Override
    public List<String> upload(MultipartFile file) {
        try {
            // 1. 生成唯一文件名并上传到OSS
            String fileName = UUID.randomUUID().toString() + ".mp3";
            String fileUrl = ossService.uploadFile(file, fileName);
            log.info("音频文件上传成功，URL：{}", fileUrl);

            // 2. 调用语音识别服务，将音频转换为文字
            String recognizedText = SpeechRecognitionUtils.recognizeSpeechByUrl(fileUrl, apiKey);
            log.info("语音识别结果：{}", recognizedText);

            // 3. 将识别的文本转换为向量
            TextSegment querySegment = TextSegment.from(recognizedText);
            Embedding queryEmbedding = embeddingModel.embed(querySegment).content();

            // 4. 在向量数据库中搜索最相关的内容
            EmbeddingSearchRequest searchRequest = EmbeddingSearchRequest.builder()
                    .queryEmbedding(queryEmbedding)
                    .maxResults(3)  // 返回最相关的3条结果
                    .minScore(0.7)  // 设置最低相似度阈值，过滤不相关结果
                    .build();

            // 5. 执行检索并返回结果
            EmbeddingSearchResult<TextSegment> searchResult = embeddingStore.search(searchRequest);

            if (!searchResult.matches().isEmpty()) {
                List<String> answers = searchResult.matches().stream()
                        .map(match -> match.embedded().text())
                        .toList();
                log.info("找到 {} 条相关答案", answers.size());
                return answers;
            }

            log.info("未找到相关答案");
            return new ArrayList<>();

        } catch (Exception e) {
            log.error("处理语音文件失败", e);
            throw new RuntimeException("语音处理失败：" + e.getMessage(), e);
        }
    }

    @Override
    public List<String> text(String text) {
        // 3. 将识别的文本转换为向量
        TextSegment querySegment = TextSegment.from(text);
        Embedding queryEmbedding = embeddingModel.embed(querySegment).content();

        // 4. 在向量数据库中搜索最相关的内容
        EmbeddingSearchRequest searchRequest = EmbeddingSearchRequest.builder()
                .queryEmbedding(queryEmbedding)
                // 返回最相关的3条结果
                .maxResults(3)
                // 设置最低相似度阈值，过滤不相关结果
                .minScore(0.7)
                .build();

        // 5. 执行检索并返回结果
        EmbeddingSearchResult<TextSegment> searchResult = embeddingStore.search(searchRequest);

        if (!searchResult.matches().isEmpty()) {
            List<String> answers = searchResult.matches().stream()
                    .map(match -> match.embedded().text())
                    .toList();
            log.info("找到 {} 条相关答案", answers.size());
            return answers;
        }

        log.info("未找到相关答案");
        return new ArrayList<>();
    }


    public void streamText(String text, StreamingChatResponseHandler streamingChatResponseHandler) {
        List<ChatMessage> chatMessageList = new ArrayList<>();
        List<String> text1 = text(text);
        chatMessageList.add(SystemMessage.from("现在你是java求职者，正在面试，请简单回答面试官问的问题，简单易懂。面试官的问题可能不准确，由实时语音翻译而来"));

        if (CollUtil.isNotEmpty(text1)) {
            StringBuilder stringBuilder = new StringBuilder();
            for (String s : text1) {
                stringBuilder.append(s);

            }
            stringBuilder.append("\n 以上知识库内容仅供参考。");
            chatMessageList.add(SystemMessage.from(stringBuilder.toString()));
        }
        chatMessageList.add(UserMessage.from("面试官问的问题:" + text));
        streamingChatModel.chat(text, streamingChatResponseHandler);
    }


    /**
     * 上传Markdown格式的知识库文件
     * 解析Markdown文件并将内容存储到向量数据库中，用于后续的语义检索
     * <p>
     * 文件格式示例：
     * ## 什么是IOC
     * <p>
     * ioc就是控制反转。传统程序，当我们需要对象时，是由代码直接new出对象...
     * <p>
     * ## 什么是AOP
     * <p>
     * aop就是面向切面编程。主要功能，把通用的业务逻辑抽象出来...
     *
     * @param file Markdown文件
     */
    public void uploadMarkdownFile(MultipartFile file) {
        try {
            log.info("开始上传知识库文件：{}", file.getOriginalFilename());

            // 1. 清空向量数据库中的旧数据
            clearOldData();

            // 2. 解析Markdown文件
            List<String> knowledgeItems = parseMarkdownFile(file);
            log.info("解析完成，共 {} 条知识条目", knowledgeItems.size());

            // 3. 将知识条目向量化并存储到数据库
            int successCount = 0;
            for (int i = 0; i < knowledgeItems.size(); i++) {
                String content = knowledgeItems.get(i);
                try {
                    log.info("正在处理第 {} 条知识：{}", i + 1, content);
                    // 将文本转换为向量
                    TextSegment textSegment = TextSegment.from(content);
                    Embedding embedding = embeddingModel.embed(textSegment).content();

                    // 存储到向量数据库
                    String result = embeddingStore.add(embedding, textSegment);
                    successCount++;
                    log.info("第 {}/{} 条知识上传成功，ID：{}", i + 1, knowledgeItems.size(), result);
                } catch (Exception e) {
                    log.error("第 {} 条知识上传失败：{}", i + 1, content, e);
                }
            }

            log.info("知识库上传完成，成功 {} 条，失败 {} 条", successCount, knowledgeItems.size() - successCount);

        } catch (Exception e) {
            log.error("上传知识库文件失败", e);
            throw new RuntimeException("知识库文件处理失败：" + e.getMessage(), e);
        }
    }

    /**
     * 清空向量数据库中的旧数据
     * 删除并重新创建集合，确保数据是最新的
     */
    private void clearOldData() {
        try {
            // 删除旧集合
            qdrantClient.deleteCollectionAsync(qdrantCollectionName).get();
            log.info("已删除旧集合：{}", qdrantCollectionName);

            // 重新创建集合
            Collections.VectorParams vectorParams = Collections.VectorParams.newBuilder()
                    .setDistance(Collections.Distance.Cosine)
                    .setSize(1024)
                    .build();
            qdrantClient.createCollectionAsync(qdrantCollectionName, vectorParams).get();
            log.info("已重新创建集合：{}", qdrantCollectionName);

        } catch (InterruptedException | ExecutionException e) {
            log.error("清空旧数据失败", e);
            throw new RuntimeException("清空旧数据失败", e);
        }
    }

    /**
     * 解析Markdown文件
     * 按照 "## 标题 + 内容" 的格式进行解析
     *
     * @param file Markdown文件
     * @return 解析后的知识条目列表，每个条目包含标题和内容
     * @throws IOException 文件读取异常
     */
    private List<String> parseMarkdownFile(MultipartFile file) throws IOException {
        List<String> knowledgeItems = new ArrayList<>();

        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(file.getInputStream(), StandardCharsets.UTF_8))) {

            String line;
            StringBuilder currentItem = new StringBuilder();
            String currentTitle = null;

            while ((line = reader.readLine()) != null) {
                // 去除首尾空格
                line = line.trim();

                // 跳过空行
                if (line.isEmpty()) {
                    continue;
                }

                // 检测是否是标题行（以 ## 开头）
                if (line.startsWith("##")) {
                    // 如果之前有内容，先保存
                    if (currentTitle != null && currentItem.length() > 0) {
                        String content = currentTitle + "\n" + currentItem.toString().trim();
                        knowledgeItems.add(content);
                        currentItem.setLength(0);  // 清空StringBuilder
                    }

                    // 提取新标题（去掉 ## 和空格）
                    currentTitle = line.substring(2).trim();

                } else {
                    // 普通内容行，追加到当前条目
                    if (currentItem.length() > 0) {
                        currentItem.append(" ");
                    }
                    currentItem.append(line);
                }
            }

            // 处理最后一个条目
            if (currentTitle != null && currentItem.length() > 0) {
                String content = currentTitle + "\n\n" + currentItem.toString().trim();
                knowledgeItems.add(content);
            }
        }

        return knowledgeItems;
    }

    @Value("${appKeyId}")
    private String appKeyId;

    @Value("${appKeySecret}")
    private String appKeySecret;

    public String getToken() {
        try {
            AccessToken accessToken = new AccessToken(appKeyId, appKeySecret);
            accessToken.apply();
            return accessToken.getToken();
        } catch (Exception e) {
            return null;
        }
    }
}
